package com.wifipositioner.positioning;

import java.util.List;

import android.net.wifi.ScanResult;

import com.wifipositioner.Globals;
import com.wifipositioner.measure.MeasuredPoint;


public class DistanceCalculatorUsingScanResult {
	
	private double closestDist = Double.MAX_VALUE;
	private String closestRefPointName = "";
	private int numOfResults = 3;
	private static String[] resultRefPointArray = new String[]{"a","b","c"};
	
	public DistanceCalculatorUsingScanResult(List<ScanResult> scanList) {
		String[] dominantSSIDs = getDominantSSIDs(scanList);
		ReferencePointMap domRefPointMap = 
			RefPointCache.get().getRefPointMapByDominantSSID(dominantSSIDs[0]);
		checkRefPointMap(domRefPointMap, scanList);
		//if the distance is too big in the dominant map
		if(closestDist > Globals.MaxDistForRefPointMatch){
			domRefPointMap = 
				RefPointCache.get().getRefPointMapBySecDominantSSID(dominantSSIDs[1]);
			checkRefPointMap(domRefPointMap, scanList);
		}
		//if the distance is too big in the secondary dominant map
		if(closestDist > Globals.MaxDistForRefPointMatch){
			checkRefPOintList(scanList);
		}
		putRefNameIntoResultArray();
		
	}
	private void checkRefPointMap(ReferencePointMap domRefPointMap, List<ScanResult> scanList) {
		if(domRefPointMap == null){
			return;
		}
		for(String domRefPointName : domRefPointMap.keySet()){
			MeasuredPoint refPoint = domRefPointMap.get(domRefPointName);
			double distance = calcDistance(scanList, refPoint);
			if(distance < closestDist){
				closestDist = distance;
				closestRefPointName = refPoint.getName();
			}
		}
	}
	private void checkRefPOintList(List<ScanResult> scanList){
		for(MeasuredPoint refPoint : RefPointCache.get().getRefPointList()){
			double distance = calcDistance(scanList, refPoint);
			if(distance < closestDist){
				closestDist = distance;
				closestRefPointName = refPoint.getName();
			}
		}
		
	}
	private double calcDistance(List<ScanResult> scanList, MeasuredPoint refPoint){
		double distance = 0;
		double distFromMissingAP = 0;
		for(ScanResult result : scanList){
			double refAP_SS;
			double variance;
			if(refPoint.scanMap.containsKey(result.SSID)){
				refAP_SS = refPoint.scanMap.get(result.SSID).getAvg();
				variance = refPoint.scanMap.get(result.SSID).getVariance();
			}
			else{
				//distFromMissingAP += Globals.missingWeight * 1 / result.level;
				continue;
			}
			double tmpDist = (refAP_SS - result.level);
			distance += tmpDist * tmpDist * getLevelWeight(result.level) / variance;
		}
		return Math.sqrt(distance) + distFromMissingAP;
	}
	public double getClosestDist() {
		return closestDist;
	}
	public String getClosestRefPointName() {
		if(resultRefPointArray[0].equals(resultRefPointArray[1])){
			return resultRefPointArray[0];
		}
		if(resultRefPointArray[0].equals(resultRefPointArray[2])){
			return resultRefPointArray[0];
		}
		if(resultRefPointArray[1].equals(resultRefPointArray[2])){
			return resultRefPointArray[1];
		}
		return resultRefPointArray[0];
	}
	public String[] getDominantSSIDs(List<ScanResult> scanList) {
		double dominantSS = -Double.MAX_VALUE;
		double secondaryDominantSS = -Double.MAX_VALUE;
		String dominantSSID = "";
		String secondaryDominantSSID = "";
		for(ScanResult result : scanList){

			if(result.level > dominantSS){
				secondaryDominantSS = dominantSS;
				secondaryDominantSSID = dominantSSID;
				dominantSS = result.level;
				dominantSSID = result.SSID;
			}
			else if(result.level > secondaryDominantSS){
				secondaryDominantSS = result.level;
				secondaryDominantSSID = result.SSID;
				
			}
		}
		return new String[]{dominantSSID, secondaryDominantSSID};
	}
	public double getLevelWeight(double level){
		if(level < -90){
			return Globals.weight90;
		}
		else if(level < -80){
			return Globals.weight80;
		}
		else if(level < -70){
			return Globals.weight70;
		}
		else if(level < -60){
			return Globals.weight60;
		}
		return 1;
	}
	private void putRefNameIntoResultArray(){
		System.arraycopy(resultRefPointArray, 0, resultRefPointArray, 1, resultRefPointArray.length-1);
		resultRefPointArray[0] = closestRefPointName;
	}

}
